﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using Tesla.Direct3D10.Graphics.Implementation;
using Tesla.Graphics;
using D3D = SlimDX.Direct3D10;

namespace Tesla.Direct3D10.Graphics {
    /// <summary>
    /// Concrete Direct3D10 implementation of <see cref="ITextureCollection"/>.
    /// </summary>
    public sealed class D3D10TextureCollection : ITextureCollection {
        private bool _vertexTex;
        private int _maxTextures;
        private Texture[] _textures;
        private bool[] _dirtyMark;
        private bool _shortCircuitUpdate;
        private D3D10Renderer _renderer;
        private D3D.Device _graphicsDevice;

        /// <summary>
        /// Gets or sets the <see cref="Tesla.Graphics.Texture"/> at the specified index.
        /// </summary>
        public Texture this[int index] {
            get {
                if(index < 0 || index >= _maxTextures) {
                    throw new ArgumentOutOfRangeException("index", "Index is out of range");
                }
                D3D10Helper.CheckDisposed(_graphicsDevice);
                return _textures[index];
            }
            set {
                if(index < 0 || index >= _maxTextures) {
                    throw new ArgumentOutOfRangeException("index", "Index is out of range");
                }
                D3D10Helper.CheckDisposed(_graphicsDevice);
                _textures[index] = value;
                _dirtyMark[index] = true;
                _shortCircuitUpdate = true;
            }
        }

        /// <summary>
        /// Gets the max textures.
        /// </summary>
        public int MaxTextures {
            get {
                return _maxTextures;
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="D3D10TextureCollection"/>.
        /// </summary>
        /// <param name="renderer">The D3D10 renderer.</param>
        /// <param name="maxTextures">The max textures count.</param>
        /// <param name="vertexTextures">True if this is meant for vertex shader textures, false for pixel shader textures.</param>
        internal D3D10TextureCollection(D3D10Renderer renderer, int maxTextures, bool vertexTextures) {
            _renderer = renderer;
            _graphicsDevice = renderer.GraphicsDevice;
            _vertexTex = vertexTextures;
            _maxTextures = maxTextures;
            _textures = new Texture[_maxTextures];
            _dirtyMark = new bool[_maxTextures];
            _shortCircuitUpdate = false;
        }

        /// <summary>
        /// Flushes the collection's state to the device, only textures that have been
        /// changed since the last draw call will get sent.
        /// </summary>
        internal void FlushState() {
            //Return if we haven't even touched these states, so we don't have to loop over them.
            if(!_shortCircuitUpdate) {
                return;
            }

            for(int i = 0; i < _maxTextures; i++) {
                //If state is dirty, grab the contents and send to the device
                if(_dirtyMark[i]) {
                    //Clear the dirty bit
                    _dirtyMark[i] = false;

                    D3D.ShaderResourceView shaderResource = null;
                    Texture value = _textures[i];
                    //Get shader resource
                    if(value != null) {
                        D3D10Helper.CheckDisposed(value);
                        switch(value.Dimensions) {
                            case TextureDimensions.One:
                                D3D10Texture1DImplementation impl1D = value.Implementation as D3D10Texture1DImplementation;
                                shaderResource = impl1D.D3D10ShaderResourceView;
                                break;
                            case TextureDimensions.Two:
                                D3D10Texture2DImplementation impl2D = value.Implementation as D3D10Texture2DImplementation;
                                shaderResource = impl2D.D3D10ShaderResourceView;
                                break;
                            case TextureDimensions.Three:
                                D3D10Texture3DImplementation impl3D = value.Implementation as D3D10Texture3DImplementation;
                                shaderResource = impl3D.D3D10ShaderResourceView;
                                break;
                            case TextureDimensions.Cube:
                                D3D10TextureCubeImplementation implCube = value.Implementation as D3D10TextureCubeImplementation;
                                shaderResource = implCube.D3D10ShaderResourceView;
                                break;
                        }
                    }
                    //Set shader resource (or null)
                    if(_vertexTex) {
                        _graphicsDevice.VertexShader.SetShaderResource(shaderResource, i);
                    } else {
                        _graphicsDevice.PixelShader.SetShaderResource(shaderResource, i);
                    }
                }
            }
            _shortCircuitUpdate = false;
        }


        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<Texture> GetEnumerator() {
            return (IEnumerator<Texture>) _textures.GetEnumerator();
        }


        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator() {
            return _textures.GetEnumerator();
        }

    }
}
